/**
 * @Title: ConfMockAspect.java
 * @Package com.kaver.mock
 * @author roc
 * @date 2019年11月25日 下午5:12:22
 * @version V1.0
 * @copyright kaver
 */
package com.kaver.mock;

import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.HashMap;
import java.util.Properties;
import java.util.Set;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.util.StringUtils;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.ValueFilter;
import com.kaver.constant.PropertyConstant;
import com.kaver.spring.AppInstance;
import com.kaver.spring.LocalCacheRepository;
import com.kaver.utils.HttpUtil;

/**
 * @ClassName: ConfMockAspect
 * @Description: mock切面
 * @author roc
 * @date 2019年11月25日 下午5:12:22
 * 
 */
@Aspect
public class ConfMockAspect {
    @Autowired
    SpelExcetor spelExcetor;
    private final static Logger logger = LoggerFactory.getLogger(ConfMockAspect.class);
    private static String mock_system_uat_url = "http://192.168.10.120:8093";
    private static String mock_system_test_url = "http://192.168.10.244:8093";
    private static String mock_system_dev_url = "http://192.168.10.103:8093";
    private static String mock_system_pre_url = "http://capital-mock.stage.kaver.com";
    
    @Pointcut("@within(com.kaver.mock.ConfMock)")
    private void aspect() {}

    @Around("aspect()")
    public Object around(JoinPoint joinPoint) throws Throwable {
        Object target = joinPoint.getTarget();
        Class<?> targetClass = target.getClass();
        Class<?>[] interfaces = targetClass.getInterfaces();
        String sName = targetClass.getSimpleName();
        MethodSignature sig = (MethodSignature)joinPoint.getSignature();
        Method method = sig.getMethod();
        String methodName = method.getName();
        Type type = method.getGenericReturnType();
        String key = "ConfMock." + sName + "." + methodName;
        // 走mock系统
        String sysKey1 = "system.ConfMock." + sName;
        String sysKey2 = "system.ConfMock." + sName + "." + methodName;
        String value = null;
        if (LocalCacheRepository.getInstance().get(sysKey1) != null
            || LocalCacheRepository.getInstance().get(sysKey2) != null) {
            String url = null;
            if (PropertyConstant.CONF_PROD_DEV.equals(AppInstance.getEnv())) {
                url = mock_system_dev_url + String.format("/api?className=%s&method=%s&paramClass=%s",
                    interfaces[0].getName(), methodName, method.getParameterTypes()[0].getName());
            } else if (PropertyConstant.CONF_PROD_TEST.equals(AppInstance.getEnv())){
                url = mock_system_test_url + String.format("/api?className=%s&method=%s&paramClass=%s",
                    interfaces[0].getName(), methodName, method.getParameterTypes()[0].getName());
            } else if (PropertyConstant.CONF_PROD_PRE.equals(AppInstance.getEnv())){
                url = mock_system_pre_url + String.format("/api?className=%s&method=%s&paramClass=%s",
                    interfaces[0].getName(), methodName, method.getParameterTypes()[0].getName());
            }  else {
                url = mock_system_uat_url + String.format("/api?className=%s&method=%s&paramClass=%s",
                    interfaces[0].getName(), methodName, method.getParameterTypes()[0].getName());
            }
            logger.info("请求资方mock项目:url=" + url);
            String param = JSON.toJSONString(joinPoint.getArgs()[0], new LengthFilter());
            logger.info("请求资方mock项目:param=" + param);
            value = HttpUtil.doPost(param, new HashMap<>(), url);
            logger.info("请求资方mock项目:result=" + JSON.toJSONString(JSON.parse(value), new LengthFilter()));
            if (value != null) {
                return JSON.parseObject(value, type);
            }
        } else {
            value = getValue(joinPoint, key);
            if (value != null) {
                logger.info("配置中心mock:key=" + key);
                Object result = parseReturn(value, type, joinPoint.getArgs());
                logger.info("配置中心mock:返回值:" + JSON.toJSONString(result));
                return result;
            }
        }
        return ((ProceedingJoinPoint)joinPoint).proceed();
    }

    // #root[1].name=xxxx
    private String getValue(JoinPoint joinPoint, String key) {
        try {
            Properties properties = LocalCacheRepository.getInstance().getProperties();
            Set<Object> set = properties.keySet();
            for (Object srt : set) {
                String keyStr = srt.toString();
                if (keyStr.startsWith(key)) {
                    String s = org.apache.commons.lang3.StringUtils.removeStart(keyStr, key);
                    if (!StringUtils.isEmpty(s)) {
                        ExpressionParser parser = new SpelExpressionParser();
                        StandardEvaluationContext context = new StandardEvaluationContext();
                        context.setRootObject(joinPoint.getArgs());
                        Boolean result =
                            (Boolean)parser.parseExpression(org.apache.commons.lang3.StringUtils.removeStart(s, "."))
                                .getValue(context);
                        if (result) {
                            return properties.getProperty(keyStr);
                        }
                    }
                }
            }
            if (properties.containsKey(key)) {
                return properties.getProperty(key);
            }
        } catch (Exception e) {
            logger.warn("解析key失败", e);
        }
        return null;
    }

    private Object parseReturn(String value, Type type, Object[] args) {
        if (String.class.getTypeName().equals(type.getTypeName())) {
            return value;
        }
        Object object = JSON.parse(value);
        if (object instanceof JSONArray) {
            JSONArray array = (JSONArray)object;
            parseValue(array, args);
            return array.toJavaObject(type);
        } else if (object instanceof JSONObject) {
            JSONObject jsonObject = (JSONObject)object;
            parseValue(jsonObject, args);
            return jsonObject.toJavaObject(type);
        }
        return object;
    }

    private void parseValue(JSONArray array, Object rootObject) {
        if (array != null && !array.isEmpty()) {
            for (int i = 0; i < array.size(); i++) {
                Object value = array.get(i);
                if (value != null) {
                    if (value instanceof String) {
                        Object newValue = doneInSpringContext((String)value, rootObject);
                        if (newValue != null) {
                            array.set(i, newValue);
                        }
                    } else if (value instanceof JSONObject) {
                        parseValue((JSONObject)value, rootObject);
                    } else if (value instanceof JSONArray) {
                        parseValue((JSONArray)value, rootObject);
                    }
                }
            }
        }
    }

    private void parseValue(JSONObject object, Object rootObject) {
        Set<String> set = object.keySet();
        if (!set.isEmpty()) {
            for (String key : set) {
                Object value = object.get(key);
                if (value != null) {
                    if (value instanceof String) {
                        if (isSpel((String)value)) {
                            Object newValue = doneInSpringContext(spelValue((String)value), rootObject);
                            if (newValue != null) {
                                object.put(key, newValue);
                            }
                        }
                    } else if (value instanceof JSONObject) {
                        parseValue((JSONObject)value, rootObject);
                    } else if (value instanceof JSONArray) {
                        parseValue((JSONArray)value, rootObject);
                    }
                }
            }
        }
    }

    private static boolean isSpel(String value) {
        // ${}
        if (value != null && value.startsWith("${") && value.endsWith("}")) {
            return true;
        }
        return false;
    }

    public static String spelValue(String value) {
        if (isSpel(value)) {
            value = org.apache.commons.lang3.StringUtils.removeFirst(value, "\\$\\{");
            value = org.apache.commons.lang3.StringUtils.removeEnd(value, "}");
            return value;
        }
        return value;
    }

    private Object doneInSpringContext(String value, Object rootObject) {
        try {
            return spelExcetor.doneInSpringContext(null, rootObject, value);
        } catch (Exception e) {
        }
        return null;
    }
    public static class LengthFilter implements ValueFilter{

        @Override
        public Object process(Object object, String name, Object value) {
            if (value != null && value instanceof String) {
                String var = (String)value;
                if (var.length() > 500) {
                    return var.substring(0, 500);
                }
            }
            return value;
        }
        
    }
    // public static class User {
    // String name;
    //
    // public String getName() {
    // return name;
    // }
    //
    // public void setName(String name) {
    // this.name = name;
    // }
    //
    // }

    public static void main(String[] args) {
        // Object[] objects = new Object[2];
        // User user1 = new User();
        // user1.name = "T(java.lang.Math).random()*10";
        // objects[0] = user1;
        // User user2 = new User();
        // user2.name = "user2";
        // objects[1] = user2;
//        ExpressionParser parser = new SpelExpressionParser();
        // StandardEvaluationContext context = new StandardEvaluationContext();
        // // context.setRootObject(objects);
        // boolean isCreatable = NumberUtils.isCreatable("00ss00001");
        // System.out.println(isCreatable);
//        String value = parser.parseExpression("2020-06-07 00:00:00").getValue(String.class);
        String value = "${2020-01-01}"; 
        System.out.println(spelValue(value));
        // // System.out.println(parser.parseExpression("#root[0].name=='user1'").getValue(context));
    }
}
